@fieldwangai/agentflow 0.1.115 → 0.1.118

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3133,6 +3133,48 @@ function hydrateWorkspaceGraphForRuntime(workspaceRoot, scoped = {}, graph = {},
3133
3133
  return hydrateWorkspaceSlotMetaFromDefinitions(workspaceRoot, scoped, withMarketplaceRuntime, userCtx);
3134
3134
  }
3135
3135
 
3136
+ function adminWorkspaceOwnerSummary(ownerId = "") {
3137
+ const id = String(ownerId || "").trim();
3138
+ if (!id) return null;
3139
+ const users = readAuthUsers();
3140
+ const knownUserIds = new Set([
3141
+ ...Object.keys(users || {}),
3142
+ ...listAgentflowUserIds(),
3143
+ ].map((value) => String(value || "").trim()).filter(Boolean));
3144
+ if (!knownUserIds.has(id)) return null;
3145
+ return {
3146
+ userId: id,
3147
+ username: String(users?.[id]?.username || id),
3148
+ };
3149
+ }
3150
+
3151
+ function adminWorkspaceRequestedUserContext(userCtx = {}) {
3152
+ const ownerId = String(userCtx.adminOwnerId || "").trim();
3153
+ if (!ownerId) return { userCtx };
3154
+ if (userCtx.isAdmin !== true) {
3155
+ return { error: "Admin permission required", status: 403 };
3156
+ }
3157
+ const owner = adminWorkspaceOwnerSummary(ownerId);
3158
+ if (!owner) return { error: "Workspace owner not found", status: 404 };
3159
+ return {
3160
+ owner,
3161
+ userCtx: {
3162
+ ...userCtx,
3163
+ userId: owner.userId,
3164
+ adminOwnerId: "",
3165
+ },
3166
+ };
3167
+ }
3168
+
3169
+ function workspaceScopedUserContext(scoped = {}, userCtx = {}) {
3170
+ if (!scoped.adminReadonly || !scoped.ownerUserId) return userCtx;
3171
+ return {
3172
+ ...userCtx,
3173
+ userId: scoped.ownerUserId,
3174
+ adminOwnerId: "",
3175
+ };
3176
+ }
3177
+
3136
3178
  function resolveWorkspaceScopeRoot(workspaceRoot, params = {}, opts = {}) {
3137
3179
  const flowId = params.flowId != null ? String(params.flowId).trim() : "";
3138
3180
  if (!flowId) return { root: path.resolve(workspaceRoot), flowId: "", flowSource: "", archived: false };
@@ -3143,6 +3185,46 @@ function resolveWorkspaceScopeRoot(workspaceRoot, params = {}, opts = {}) {
3143
3185
  if (!isValidFlowSourceRead(flowSource)) {
3144
3186
  return { root: "", error: "Invalid flowSource" };
3145
3187
  }
3188
+ const adminOwnerId = String(params.adminOwnerId || opts.adminOwnerId || "").trim();
3189
+ if (adminOwnerId) {
3190
+ if (opts.isAdmin !== true) {
3191
+ return { root: "", error: "Admin permission required", status: 403 };
3192
+ }
3193
+ if (flowSource !== "user") {
3194
+ return { root: "", error: "Admin read-only review only supports user projects", status: 400 };
3195
+ }
3196
+ const owner = adminWorkspaceOwnerSummary(adminOwnerId);
3197
+ if (!owner) {
3198
+ return { root: "", error: "Workspace owner not found", status: 404 };
3199
+ }
3200
+ const targetFlow = listFlowsJson(workspaceRoot, { userId: owner.userId })
3201
+ .find((flow) => (
3202
+ flow.id === flowId
3203
+ && (flow.source || "user") === "user"
3204
+ && Boolean(flow.archived) === archived
3205
+ ));
3206
+ if (!targetFlow?.path) {
3207
+ return { root: "", error: "Pipeline workspace not found", status: 404 };
3208
+ }
3209
+ return {
3210
+ root: path.resolve(targetFlow.path),
3211
+ flowId,
3212
+ flowSource: "user",
3213
+ requestedFlowSource: flowSource,
3214
+ workspaceId: "",
3215
+ archived,
3216
+ collaboration: null,
3217
+ collaborationAccess: {
3218
+ allowed: true,
3219
+ writable: false,
3220
+ runnable: false,
3221
+ role: "admin-viewer",
3222
+ },
3223
+ adminReadonly: true,
3224
+ ownerUserId: owner.userId,
3225
+ ownerUsername: owner.username,
3226
+ };
3227
+ }
3146
3228
  const workspaceId = String(params.workspaceId || "").trim();
3147
3229
  let collaboration = getWorkspaceCollaborationForProject({
3148
3230
  workspaceId,
@@ -3255,9 +3337,11 @@ function prdWorkflowShareLinkSummary(record, shareToken, publicBaseUrl, userId =
3255
3337
  workflowShare: token,
3256
3338
  });
3257
3339
  const base = String(publicBaseUrl || "").replace(/\/+$/, "");
3340
+ const shortUrl = `${base}/w/${encodeURIComponent(token)}`;
3258
3341
  return {
3259
3342
  tapdId: String(record.tapdId || ""),
3260
3343
  url: `${base}/workspace?${query.toString()}`,
3344
+ shortUrl,
3261
3345
  active: true,
3262
3346
  readOnly: true,
3263
3347
  createdAt: record.shareCreatedAt || "",
@@ -7472,6 +7556,17 @@ function resolvePrdWorkflowScope(workspaceRoot, params = {}, userCtx = {}, capab
7472
7556
  const flowId = String(params.flowId || "").trim();
7473
7557
  const flowSource = String(params.flowSource || "user").trim() || "user";
7474
7558
  const archived = params.archived === true || params.archived === "1" || params.flowArchived === true;
7559
+ const adminOwnerId = String(params.adminOwnerId || userCtx.adminOwnerId || "").trim();
7560
+ if (adminOwnerId && userCtx.isAdmin !== true) {
7561
+ return { error: "Admin permission required", status: 403 };
7562
+ }
7563
+ const adminOwner = adminOwnerId ? adminWorkspaceOwnerSummary(adminOwnerId) : null;
7564
+ if (adminOwnerId && !adminOwner) {
7565
+ return { error: "Workspace owner not found", status: 404 };
7566
+ }
7567
+ if (adminOwner && capability !== "read") {
7568
+ return { error: "Admin Workspace review is read-only", status: 403 };
7569
+ }
7475
7570
  const shareToken = String(params.workflowShare || params.workflow_share || "").trim();
7476
7571
  const linkCollaboration = shareToken ? getPrdWorkflowCollaborationByShareToken(shareToken) : null;
7477
7572
  if (shareToken && (!linkCollaboration || linkCollaboration.tapdId !== tapdId)) {
@@ -7480,8 +7575,10 @@ function resolvePrdWorkflowScope(workspaceRoot, params = {}, userCtx = {}, capab
7480
7575
  const memberCollaboration = tapdId
7481
7576
  ? getPrdWorkflowCollaborationForUser(tapdId, userCtx?.userId)
7482
7577
  : null;
7483
- const collaboration = linkCollaboration || memberCollaboration;
7484
- const access = linkCollaboration
7578
+ const collaboration = adminOwner ? null : (linkCollaboration || memberCollaboration);
7579
+ const access = adminOwner
7580
+ ? { allowed: true, writable: false, role: "admin-viewer", via: "admin-review" }
7581
+ : linkCollaboration
7485
7582
  ? { allowed: true, writable: false, role: "viewer", via: "share-link" }
7486
7583
  : prdWorkflowCollaborationAccess(collaboration, userCtx?.userId);
7487
7584
  if (collaboration && !access.allowed) {
@@ -7490,7 +7587,7 @@ function resolvePrdWorkflowScope(workspaceRoot, params = {}, userCtx = {}, capab
7490
7587
  if (capability === "write" && (linkCollaboration || (collaboration && !access.writable))) {
7491
7588
  return { error: "PRD Workflow collaboration edit permission denied", status: 403 };
7492
7589
  }
7493
- const ownerId = String(collaboration?.ownerId || userCtx?.userId || "").trim();
7590
+ const ownerId = String(adminOwner?.userId || collaboration?.ownerId || userCtx?.userId || "").trim();
7494
7591
  const stateRoot = path.resolve(getAgentflowUserDataRoot(ownerId));
7495
7592
  let executionRoot = path.resolve(workspaceRoot);
7496
7593
  if (flowId) {
@@ -7498,6 +7595,7 @@ function resolvePrdWorkflowScope(workspaceRoot, params = {}, userCtx = {}, capab
7498
7595
  flowId,
7499
7596
  flowSource,
7500
7597
  workspaceId: params.workspaceId || "",
7598
+ adminOwnerId,
7501
7599
  archived,
7502
7600
  }, userCtx);
7503
7601
  if (projectScope.error) {
@@ -7516,6 +7614,7 @@ function resolvePrdWorkflowScope(workspaceRoot, params = {}, userCtx = {}, capab
7516
7614
  collaborationAccess: access,
7517
7615
  shareToken,
7518
7616
  sharedByLink: Boolean(linkCollaboration),
7617
+ adminReadonly: Boolean(adminOwner),
7519
7618
  flowId,
7520
7619
  flowSource,
7521
7620
  archived,
@@ -7526,7 +7625,8 @@ function prdWorkflowKey(userCtx = {}, flowSource = "user", flowId = "", tapdId =
7526
7625
  const id = String(tapdId || "").trim();
7527
7626
  const collaboration = getPrdWorkflowCollaborationByShareToken(shareToken)
7528
7627
  || getPrdWorkflowCollaborationForUser(id, userCtx?.userId);
7529
- const actorScope = `user:${String(collaboration?.ownerId || userCtx?.userId || "")}`;
7628
+ const adminOwnerId = userCtx?.isAdmin === true ? String(userCtx?.adminOwnerId || "").trim() : "";
7629
+ const actorScope = `user:${String(collaboration?.ownerId || adminOwnerId || userCtx?.userId || "")}`;
7530
7630
  return [actorScope, id].join("\t");
7531
7631
  }
7532
7632
 
@@ -7678,6 +7778,32 @@ function prdWorkflowReviewIdFromRequest(tapdId, payload = {}, durability = "temp
7678
7778
  return prdWorkflowSafeStateId(["review", tapdId, stageHead, digest].filter(Boolean).join("-")).slice(0, 64);
7679
7779
  }
7680
7780
 
7781
+ function prdWorkflowReviewArtifactKey(tapdId, payload = {}, durability = "temporary") {
7782
+ const explicit = String(payload.artifactKey || payload.artifact_key || "").trim();
7783
+ if (explicit) return explicit.slice(0, 500);
7784
+ const issueKey = prdWorkflowSafeStateId(
7785
+ payload.issueKey || payload.issue_key || payload.issue || "global",
7786
+ ).toLowerCase();
7787
+ const platform = prdWorkflowSafeStateId(payload.platform || "all").toLowerCase();
7788
+ const stage = prdWorkflowSafeStateId(
7789
+ payload.stage
7790
+ || payload.stageKey
7791
+ || payload.stage_key
7792
+ || payload.action
7793
+ || payload.actionId
7794
+ || payload.action_id
7795
+ || "review",
7796
+ ).toLowerCase();
7797
+ return [
7798
+ "prd-review",
7799
+ prdWorkflowSafeStateId(tapdId).toLowerCase(),
7800
+ issueKey,
7801
+ platform,
7802
+ stage,
7803
+ String(durability || "temporary").trim().toLowerCase() || "temporary",
7804
+ ].join(":").slice(0, 500);
7805
+ }
7806
+
7681
7807
  function prdWorkflowStatePath(scopedRoot, tapdId) {
7682
7808
  const rootDir = scopedRoot || process.cwd();
7683
7809
  return path.join(rootDir, ".workspace", "prd-flow", "workflow-state", `${prdWorkflowSafeStateId(tapdId)}.json`);
@@ -9131,18 +9257,27 @@ function prdWorkflowNormalizeRuntimeEvent(tapdId, event = {}) {
9131
9257
 
9132
9258
  function prdWorkflowMergeRuntimeEventArrays(left, right) {
9133
9259
  const out = [];
9134
- const seen = new Set();
9260
+ const seen = new Map();
9135
9261
  for (const value of [...(Array.isArray(left) ? left : []), ...(Array.isArray(right) ? right : [])]) {
9136
9262
  if (value == null) continue;
9137
- let key = "";
9263
+ const explicitKey = value && typeof value === "object" && !Array.isArray(value)
9264
+ ? String(value.key || value.artifactKey || value.artifact_key || "").trim()
9265
+ : "";
9266
+ let key = explicitKey ? `key:${explicitKey}` : "";
9138
9267
  try {
9139
- key = typeof value === "string" ? value : JSON.stringify(value);
9268
+ if (!key) key = typeof value === "string" ? value : JSON.stringify(value);
9140
9269
  } catch {
9141
9270
  key = String(value);
9142
9271
  }
9143
- if (seen.has(key)) continue;
9144
- seen.add(key);
9145
- out.push(value);
9272
+ const index = seen.get(key);
9273
+ if (index == null) {
9274
+ seen.set(key, out.length);
9275
+ out.push(value);
9276
+ continue;
9277
+ }
9278
+ if (explicitKey) {
9279
+ out[index] = value;
9280
+ }
9146
9281
  }
9147
9282
  return out;
9148
9283
  }
@@ -10958,7 +11093,45 @@ export function startUiServer({
10958
11093
  }
10959
11094
 
10960
11095
  const authUser = getAuthUserFromRequest(req);
10961
- const userCtx = authUser ? { userId: authUser.userId, isAdmin: Boolean(authUser.isAdmin) } : {};
11096
+ const userCtx = authUser ? {
11097
+ userId: authUser.userId,
11098
+ isAdmin: Boolean(authUser.isAdmin),
11099
+ adminOwnerId: String(url.searchParams.get("adminOwnerId") || "").trim(),
11100
+ } : {};
11101
+ if (req.method === "GET" && url.pathname.startsWith("/w/")) {
11102
+ const parts = url.pathname.split("/").filter(Boolean);
11103
+ if (parts.length !== 2) {
11104
+ res.writeHead(404);
11105
+ res.end("Not found");
11106
+ return;
11107
+ }
11108
+ let shareToken = "";
11109
+ try {
11110
+ shareToken = decodeURIComponent(parts[1] || "");
11111
+ } catch {
11112
+ res.writeHead(404);
11113
+ res.end("Not found");
11114
+ return;
11115
+ }
11116
+ const record = getPrdWorkflowCollaborationByShareToken(shareToken);
11117
+ if (!record) {
11118
+ res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
11119
+ res.end("Workflow share link is invalid or has been revoked");
11120
+ return;
11121
+ }
11122
+ const query = new URLSearchParams({
11123
+ view: "workflow",
11124
+ tapdId: String(record.tapdId || ""),
11125
+ workflowShare: shareToken,
11126
+ });
11127
+ res.writeHead(302, {
11128
+ Location: `/workspace?${query.toString()}`,
11129
+ "Cache-Control": "no-store",
11130
+ "Referrer-Policy": "no-referrer",
11131
+ });
11132
+ res.end();
11133
+ return;
11134
+ }
10962
11135
  if (req.method === "GET" && url.pathname === "/api/auth/session-token") {
10963
11136
  if (!authUser?.userId) {
10964
11137
  json(res, 401, { error: "Unauthorized" });
@@ -11281,7 +11454,7 @@ export function startUiServer({
11281
11454
  json(res, 200, {
11282
11455
  ok: true,
11283
11456
  snapshot,
11284
- ...(workflowShare ? { workflowShare, shareUrl: workflowShare.url } : {}),
11457
+ ...(workflowShare ? { workflowShare, shareUrl: workflowShare.shortUrl || workflowShare.url } : {}),
11285
11458
  });
11286
11459
  } catch (e) {
11287
11460
  json(res, 500, { error: (e && e.message) || String(e) });
@@ -11470,7 +11643,7 @@ export function startUiServer({
11470
11643
  json(res, 200, {
11471
11644
  ok: true,
11472
11645
  snapshot: withDiagnostic,
11473
- ...(workflowShare ? { workflowShare, shareUrl: workflowShare.url } : {}),
11646
+ ...(workflowShare ? { workflowShare, shareUrl: workflowShare.shortUrl || workflowShare.url } : {}),
11474
11647
  });
11475
11648
  } catch (e) {
11476
11649
  json(res, 500, { error: (e && e.message) || String(e) });
@@ -12195,10 +12368,15 @@ export function startUiServer({
12195
12368
  const shortUrl = shortLink?.shortUrl || "";
12196
12369
  const displayUrl = shortUrl || reviewUrl;
12197
12370
  const durability = review.durability || "temporary";
12371
+ const artifactKey = prdWorkflowReviewArtifactKey(tapdId, payload, durability);
12198
12372
  const reviewSource = review.source && typeof review.source === "object" && !Array.isArray(review.source)
12199
12373
  ? review.source
12200
12374
  : { kind: durability === "durable" ? "ai-doc" : "local-draft", durability };
12375
+ const idempotencyKey = String(
12376
+ payload.idempotencyKey || payload.idempotency_key || "",
12377
+ ).trim();
12201
12378
  const artifact = {
12379
+ key: artifactKey,
12202
12380
  label: payload.artifactLabel || "Markdown Review",
12203
12381
  kind: durability === "temporary" ? "temporary-review" : "review",
12204
12382
  persistence: "runtime",
@@ -12222,12 +12400,15 @@ export function startUiServer({
12222
12400
  detail: "已生成临时 Markdown review 链接",
12223
12401
  status: "current",
12224
12402
  issueKey: payload.issueKey || payload.issue_key || "",
12403
+ idempotencyKey,
12225
12404
  durability,
12226
12405
  sourceArtifact: reviewSource,
12227
12406
  expiresAt: review.expiresAt || "",
12228
12407
  artifacts: [artifact],
12229
12408
  links: [{
12409
+ key: artifactKey,
12230
12410
  label: "Markdown Review",
12411
+ kind: artifact.kind,
12231
12412
  url: displayUrl,
12232
12413
  canonicalUrl: reviewUrl,
12233
12414
  shortUrl,
@@ -12647,6 +12828,36 @@ export function startUiServer({
12647
12828
  return;
12648
12829
  }
12649
12830
 
12831
+ if (req.method === "GET" && url.pathname === "/api/admin/user-workspaces") {
12832
+ if (!authUser?.isAdmin) {
12833
+ json(res, 403, { error: "Admin permission required" });
12834
+ return;
12835
+ }
12836
+ const owner = adminWorkspaceOwnerSummary(url.searchParams.get("userId") || "");
12837
+ if (!owner) {
12838
+ json(res, 404, { error: "Workspace owner not found" });
12839
+ return;
12840
+ }
12841
+ try {
12842
+ const workspaces = listFlowsJson(root, { userId: owner.userId })
12843
+ .filter((flow) => (flow.source || "user") === "user")
12844
+ .map((flow) => ({
12845
+ id: String(flow.id || ""),
12846
+ source: "user",
12847
+ archived: flow.archived === true,
12848
+ description: String(flow.description || ""),
12849
+ ownerUserId: owner.userId,
12850
+ ownerUsername: owner.username,
12851
+ adminReadonly: true,
12852
+ }))
12853
+ .sort((a, b) => Number(a.archived) - Number(b.archived) || a.id.localeCompare(b.id));
12854
+ json(res, 200, { owner, workspaces });
12855
+ } catch (e) {
12856
+ json(res, 500, { error: (e && e.message) || String(e) });
12857
+ }
12858
+ return;
12859
+ }
12860
+
12650
12861
  if (req.method === "GET" && url.pathname === "/api/admin/run-detail") {
12651
12862
  if (!authUser?.isAdmin) {
12652
12863
  json(res, 403, { error: "Admin permission required" });
@@ -13022,12 +13233,17 @@ export function startUiServer({
13022
13233
  flowId,
13023
13234
  flowSource,
13024
13235
  workspaceId: payload.workspaceId || "",
13236
+ adminOwnerId: payload.adminOwnerId || "",
13025
13237
  archived,
13026
13238
  }, userCtx);
13027
13239
  if (scoped.error) {
13028
13240
  json(res, scoped.status || 400, { error: scoped.error });
13029
13241
  return;
13030
13242
  }
13243
+ if (scoped.adminReadonly) {
13244
+ json(res, 403, { error: "Admin Workspace review is read-only" });
13245
+ return;
13246
+ }
13031
13247
  const ensured = ensureWorkspaceCollaboration({
13032
13248
  flowId,
13033
13249
  flowSource,
@@ -13139,7 +13355,12 @@ export function startUiServer({
13139
13355
  json(res, scoped.status || 400, { error: scoped.error });
13140
13356
  return;
13141
13357
  }
13142
- const key = workspaceCollaborationEventKey(userCtx, scoped.flowSource, scoped.flowId, scoped.archived);
13358
+ const key = workspaceCollaborationEventKey(
13359
+ workspaceScopedUserContext(scoped, userCtx),
13360
+ scoped.flowSource,
13361
+ scoped.flowId,
13362
+ scoped.archived,
13363
+ );
13143
13364
  let subscribers = workspaceCollaborationSubscribers.get(key);
13144
13365
  if (!subscribers) {
13145
13366
  subscribers = new Set();
@@ -13256,7 +13477,8 @@ export function startUiServer({
13256
13477
  return;
13257
13478
  }
13258
13479
  const { path: graphPath, graph } = readWorkspaceGraph(scoped.root);
13259
- const hydratedGraph = hydrateWorkspaceGraphForRuntime(root, scoped, graph, userCtx);
13480
+ const scopedUserCtx = workspaceScopedUserContext(scoped, userCtx);
13481
+ const hydratedGraph = hydrateWorkspaceGraphForRuntime(root, scoped, graph, scopedUserCtx);
13260
13482
  const collaborationAccess = scoped.collaborationAccess || workspaceCollaborationAccess(null, userCtx.userId);
13261
13483
  json(res, 200, {
13262
13484
  ok: true,
@@ -13270,9 +13492,15 @@ export function startUiServer({
13270
13492
  flowSource: scoped.flowSource,
13271
13493
  archived: scoped.archived,
13272
13494
  writable: !(scoped.archived || isReadonlyBuiltinFlowSource(scoped.flowSource))
13495
+ && scoped.adminReadonly !== true
13273
13496
  && collaborationAccess.writable !== false,
13274
13497
  collaboration: workspaceCollaborationSummaryWithUsers(scoped.collaboration, userCtx.userId),
13275
- workspaceSchedules: listWorkspaceScheduleStatusesForFlow(userCtx, scoped.flowSource || "user", scoped.flowId || ""),
13498
+ adminReview: scoped.adminReadonly ? {
13499
+ readonly: true,
13500
+ ownerUserId: scoped.ownerUserId,
13501
+ ownerUsername: scoped.ownerUsername,
13502
+ } : null,
13503
+ workspaceSchedules: listWorkspaceScheduleStatusesForFlow(scopedUserCtx, scoped.flowSource || "user", scoped.flowId || ""),
13276
13504
  });
13277
13505
  } catch (e) {
13278
13506
  json(res, 500, { error: (e && e.message) || String(e) });
@@ -13293,12 +13521,17 @@ export function startUiServer({
13293
13521
  flowId: payload.flowId || "",
13294
13522
  flowSource: payload.flowSource || "user",
13295
13523
  workspaceId: payload.workspaceId || "",
13524
+ adminOwnerId: payload.adminOwnerId || "",
13296
13525
  archived: payload.archived === true || payload.flowArchived === true,
13297
13526
  }, userCtx);
13298
13527
  if (scoped.error) {
13299
13528
  json(res, 400, { error: scoped.error });
13300
13529
  return;
13301
13530
  }
13531
+ if (scoped.adminReadonly) {
13532
+ json(res, 403, { error: "Admin Workspace review is read-only" });
13533
+ return;
13534
+ }
13302
13535
  const { graph } = readWorkspaceGraph(scoped.root);
13303
13536
  const nodeIds = normalizeDisplayShareNodeIds(payload.nodeIds, graph);
13304
13537
  if (nodeIds.length === 0) {
@@ -13338,6 +13571,7 @@ export function startUiServer({
13338
13571
  flowId: payload.flowId || "",
13339
13572
  flowSource: payload.flowSource || "user",
13340
13573
  workspaceId: payload.workspaceId || "",
13574
+ adminOwnerId: payload.adminOwnerId || "",
13341
13575
  archived: payload.archived === true || payload.flowArchived === true,
13342
13576
  }, userCtx);
13343
13577
  if (scoped.error) {
@@ -13451,8 +13685,21 @@ export function startUiServer({
13451
13685
  json(res, 400, { error: "Missing flowId" });
13452
13686
  return;
13453
13687
  }
13688
+ const scoped = resolveWorkspaceScopeRoot(root, {
13689
+ flowId,
13690
+ flowSource,
13691
+ archived: url.searchParams.get("archived") === "1",
13692
+ }, userCtx);
13693
+ if (scoped.error) {
13694
+ json(res, scoped.status || 400, { error: scoped.error });
13695
+ return;
13696
+ }
13454
13697
  json(res, 200, {
13455
- schedules: listWorkspaceScheduleStatusesForFlow(userCtx, flowSource, flowId),
13698
+ schedules: listWorkspaceScheduleStatusesForFlow(
13699
+ workspaceScopedUserContext(scoped, userCtx),
13700
+ flowSource,
13701
+ flowId,
13702
+ ),
13456
13703
  });
13457
13704
  } catch (e) {
13458
13705
  json(res, 500, { error: (e && e.message) || String(e) });
@@ -13473,12 +13720,17 @@ export function startUiServer({
13473
13720
  flowId: payload.flowId || "",
13474
13721
  flowSource: payload.flowSource || "user",
13475
13722
  workspaceId: payload.workspaceId || "",
13723
+ adminOwnerId: payload.adminOwnerId || "",
13476
13724
  archived: payload.archived === true || payload.flowArchived === true,
13477
13725
  }, userCtx);
13478
13726
  if (scoped.error) {
13479
13727
  json(res, 400, { error: scoped.error });
13480
13728
  return;
13481
13729
  }
13730
+ if (scoped.collaborationAccess?.runnable === false) {
13731
+ json(res, 403, { error: "Workspace run permission denied" });
13732
+ return;
13733
+ }
13482
13734
  const flowId = String(payload.flowId || "").trim();
13483
13735
  if (!flowId) {
13484
13736
  json(res, 400, { error: "Missing flowId" });
@@ -13521,6 +13773,7 @@ export function startUiServer({
13521
13773
  flowId: payload.flowId || "",
13522
13774
  flowSource: payload.flowSource || "user",
13523
13775
  workspaceId: payload.workspaceId || "",
13776
+ adminOwnerId: payload.adminOwnerId || "",
13524
13777
  archived: payload.archived === true || payload.flowArchived === true,
13525
13778
  }, userCtx);
13526
13779
  if (scoped.error) {
@@ -13585,6 +13838,7 @@ export function startUiServer({
13585
13838
  flowId: payload.flowId || "",
13586
13839
  flowSource: payload.flowSource || "user",
13587
13840
  workspaceId: payload.workspaceId || "",
13841
+ adminOwnerId: payload.adminOwnerId || "",
13588
13842
  archived: payload.archived === true || payload.flowArchived === true,
13589
13843
  }, userCtx);
13590
13844
  if (scoped.error) {
@@ -13868,9 +14122,10 @@ export function startUiServer({
13868
14122
  json(res, scoped.status || 400, { error: scoped.error });
13869
14123
  return;
13870
14124
  }
14125
+ const scopedUserCtx = workspaceScopedUserContext(scoped, userCtx);
13871
14126
  json(res, 200, {
13872
14127
  runs: listWorkspaceRunLogs({
13873
- userId: flowSource === "workspace" ? "" : userCtx.userId || "",
14128
+ userId: flowSource === "workspace" ? "" : scopedUserCtx.userId || "",
13874
14129
  flowId,
13875
14130
  flowSource,
13876
14131
  scheduleNodeId,
@@ -13902,8 +14157,9 @@ export function startUiServer({
13902
14157
  json(res, scoped.status || 400, { error: scoped.error });
13903
14158
  return;
13904
14159
  }
14160
+ const scopedUserCtx = workspaceScopedUserContext(scoped, userCtx);
13905
14161
  const run = listWorkspaceRunLogs({
13906
- userId: flowSource === "workspace" ? "" : userCtx.userId || "",
14162
+ userId: flowSource === "workspace" ? "" : scopedUserCtx.userId || "",
13907
14163
  flowId,
13908
14164
  flowSource,
13909
14165
  limit: 200,
@@ -13939,7 +14195,7 @@ export function startUiServer({
13939
14195
  json(res, scoped.status || 400, { error: scoped.error });
13940
14196
  return;
13941
14197
  }
13942
- const scopeKey = workspaceRunKey(userCtx, flowSource, flowId);
14198
+ const scopeKey = workspaceRunKey(workspaceScopedUserContext(scoped, userCtx), flowSource, flowId);
13943
14199
  const entries = workspaceActiveRunsForScope(scopeKey).map(([, entry]) => entry);
13944
14200
  const entry = entries[0] || null;
13945
14201
  json(res, 200, {
@@ -13980,6 +14236,7 @@ export function startUiServer({
13980
14236
  const scoped = resolveWorkspaceScopeRoot(root, {
13981
14237
  flowId,
13982
14238
  flowSource,
14239
+ adminOwnerId: payload.adminOwnerId || "",
13983
14240
  archived: payload.archived === true || payload.flowArchived === true,
13984
14241
  }, userCtx);
13985
14242
  if (scoped.error) {
@@ -14124,6 +14381,7 @@ export function startUiServer({
14124
14381
  const scoped = resolveWorkspaceScopeRoot(root, {
14125
14382
  flowId: payload.flowId || "",
14126
14383
  flowSource: payload.flowSource || "user",
14384
+ adminOwnerId: payload.adminOwnerId || "",
14127
14385
  archived: payload.archived === true || payload.flowArchived === true,
14128
14386
  }, userCtx);
14129
14387
  if (scoped.error) {
@@ -14185,6 +14443,7 @@ export function startUiServer({
14185
14443
  const scoped = resolveWorkspaceScopeRoot(root, {
14186
14444
  flowId: payload.flowId || "",
14187
14445
  flowSource: payload.flowSource || "user",
14446
+ adminOwnerId: payload.adminOwnerId || "",
14188
14447
  archived: payload.archived === true || payload.flowArchived === true,
14189
14448
  }, userCtx);
14190
14449
  if (scoped.error) {
@@ -14259,6 +14518,7 @@ export function startUiServer({
14259
14518
  const scoped = resolveWorkspaceScopeRoot(root, {
14260
14519
  flowId: parsed.fields.flowId || "",
14261
14520
  flowSource: parsed.fields.flowSource || "user",
14521
+ adminOwnerId: parsed.fields.adminOwnerId || "",
14262
14522
  archived: parsed.fields.archived === "1" || parsed.fields.archived === "true" || parsed.fields.flowArchived === "true",
14263
14523
  }, userCtx);
14264
14524
  if (scoped.error) {
@@ -14304,6 +14564,7 @@ export function startUiServer({
14304
14564
  const scoped = resolveWorkspaceScopeRoot(root, {
14305
14565
  flowId: payload.flowId || "",
14306
14566
  flowSource: payload.flowSource || "user",
14567
+ adminOwnerId: payload.adminOwnerId || "",
14307
14568
  archived: payload.archived === true || payload.flowArchived === true,
14308
14569
  }, userCtx);
14309
14570
  if (scoped.error) {
@@ -14344,6 +14605,7 @@ export function startUiServer({
14344
14605
  const scoped = resolveWorkspaceScopeRoot(root, {
14345
14606
  flowId: payload.flowId || "",
14346
14607
  flowSource: payload.flowSource || "user",
14608
+ adminOwnerId: payload.adminOwnerId || "",
14347
14609
  archived: payload.archived === true || payload.flowArchived === true,
14348
14610
  }, userCtx);
14349
14611
  if (scoped.error) {
@@ -14394,6 +14656,7 @@ export function startUiServer({
14394
14656
  const scoped = resolveWorkspaceScopeRoot(root, {
14395
14657
  flowId: req.method === "POST" ? (payload.flowId || "") : (url.searchParams.get("flowId") || ""),
14396
14658
  flowSource: req.method === "POST" ? (payload.flowSource || "user") : (url.searchParams.get("flowSource") || "user"),
14659
+ adminOwnerId: req.method === "POST" ? (payload.adminOwnerId || "") : "",
14397
14660
  archived: req.method === "POST"
14398
14661
  ? (payload.archived === true || payload.flowArchived === true)
14399
14662
  : (url.searchParams.get("archived") === "1" || url.searchParams.get("flowArchived") === "1"),
@@ -14435,6 +14698,7 @@ export function startUiServer({
14435
14698
  const scoped = resolveWorkspaceScopeRoot(root, {
14436
14699
  flowId: payload.flowId || "",
14437
14700
  flowSource: payload.flowSource || "user",
14701
+ adminOwnerId: payload.adminOwnerId || "",
14438
14702
  archived: payload.archived === true || payload.flowArchived === true,
14439
14703
  }, userCtx);
14440
14704
  if (scoped.error) {
@@ -14525,6 +14789,7 @@ export function startUiServer({
14525
14789
  const scoped = resolveWorkspaceScopeRoot(root, {
14526
14790
  flowId: payload.flowId || "",
14527
14791
  flowSource: payload.flowSource || "user",
14792
+ adminOwnerId: payload.adminOwnerId || "",
14528
14793
  archived: payload.archived === true || payload.flowArchived === true,
14529
14794
  }, userCtx);
14530
14795
  if (scoped.error) {
@@ -15168,9 +15433,18 @@ export function startUiServer({
15168
15433
  }
15169
15434
  const nodesArchived = url.searchParams.get("archived") === "1";
15170
15435
  try {
15436
+ const requestedContext = adminWorkspaceRequestedUserContext(userCtx);
15437
+ if (requestedContext.error) {
15438
+ json(res, requestedContext.status || 403, { error: requestedContext.error });
15439
+ return;
15440
+ }
15171
15441
  const { setLanguage } = await import("./i18n.mjs");
15172
15442
  setLanguage(lang);
15173
- json(res, 200, listNodesJson(root, flowId || "", flowId ? flowSource : "", { archived: nodesArchived, ...userCtx, marketplaceScope }));
15443
+ json(res, 200, listNodesJson(root, flowId || "", flowId ? flowSource : "", {
15444
+ archived: nodesArchived,
15445
+ ...requestedContext.userCtx,
15446
+ marketplaceScope,
15447
+ }));
15174
15448
  } catch (e) {
15175
15449
  json(res, 500, { error: (e && e.message) || String(e) });
15176
15450
  }
@@ -15191,7 +15465,15 @@ export function startUiServer({
15191
15465
  }
15192
15466
  const archived = url.searchParams.get("archived") === "1";
15193
15467
  try {
15194
- const detail = readNodeDetailJson(root, nodeId, flowId, flowId ? (flowSource || "user") : "", { archived, ...userCtx });
15468
+ const requestedContext = adminWorkspaceRequestedUserContext(userCtx);
15469
+ if (requestedContext.error) {
15470
+ json(res, requestedContext.status || 403, { error: requestedContext.error });
15471
+ return;
15472
+ }
15473
+ const detail = readNodeDetailJson(root, nodeId, flowId, flowId ? (flowSource || "user") : "", {
15474
+ archived,
15475
+ ...requestedContext.userCtx,
15476
+ });
15195
15477
  if (detail.error) {
15196
15478
  json(res, 404, { error: detail.error });
15197
15479
  return;
@@ -15438,7 +15720,12 @@ export function startUiServer({
15438
15720
  json(res, collaborationDenied.status, { error: collaborationDenied.error });
15439
15721
  return;
15440
15722
  }
15441
- const result = readFlowJson(root, flowId, flowSource, { archived: flowArchived, ...userCtx });
15723
+ const requestedContext = adminWorkspaceRequestedUserContext(userCtx);
15724
+ if (requestedContext.error) {
15725
+ json(res, requestedContext.status || 403, { error: requestedContext.error });
15726
+ return;
15727
+ }
15728
+ const result = readFlowJson(root, flowId, flowSource, { archived: flowArchived, ...requestedContext.userCtx });
15442
15729
  if (result.error) {
15443
15730
  json(res, 404, result);
15444
15731
  return;